package cc.sjfx.sharedoc.widget.view.floatingactionbutton;

import cc.sjfx.sharedoc.R;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.TextView;

public class FloatingActionsMenu extends ViewGroup {
	public static final int EXPAND_UP = 0;
	public static final int EXPAND_DOWN = 1;
	public static final int EXPAND_LEFT = 2;
	public static final int EXPAND_RIGHT = 3;
	int screenHeight, screenWidth, lastX, lastY;
	public static final int LABELS_ON_LEFT_SIDE = 0;
	public static final int LABELS_ON_RIGHT_SIDE = 1;

	private static final int ANIMATION_DURATION = 300;
	private static final float COLLAPSED_PLUS_ROTATION = 0f;
	private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;

	private int mAddButtonPlusColor;
	private int mAddButtonColorNormal;
	private int mAddButtonColorPressed;
	private int mAddButtonSize;
	private boolean mAddButtonStrokeVisible;
	private int mExpandDirection;

	private int mButtonSpacing;
	private int mLabelsMargin;
	private int mLabelsVerticalOffset;

	private boolean mExpanded;

	private AnimatorSet mExpandAnimation = new AnimatorSet()
			.setDuration(ANIMATION_DURATION);
	private AnimatorSet mCollapseAnimation = new AnimatorSet()
			.setDuration(ANIMATION_DURATION);
	private AddFloatingActionButton mAddButton;
	private RotatingDrawable mRotatingDrawable;
	private int mMaxButtonWidth;
	private int mMaxButtonHeight;
	private int mLabelsStyle;
	private int mLabelsPosition;
	private int mButtonsCount;
	private TouchDelegateGroup mTouchDelegateGroup;

	private OnFloatingActionsMenuUpdateListener mListener;

	public interface OnFloatingActionsMenuUpdateListener {
		void onMenuExpanded();

		void onMenuCollapsed();
	}

	public FloatingActionsMenu(Context context) {
		this(context, null);
	}

	public FloatingActionsMenu(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context, attrs);
	}

	public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context, attrs);
	}

	private void init(Context context, AttributeSet attributeSet) {
		mButtonSpacing = (int) (getResources().getDimension(
				R.dimen.fab_actions_spacing)
				- getResources().getDimension(R.dimen.fab_shadow_radius) - getResources()
				.getDimension(R.dimen.fab_shadow_offset));
		mLabelsMargin = getResources().getDimensionPixelSize(
				R.dimen.fab_labels_margin);
		mLabelsVerticalOffset = getResources().getDimensionPixelSize(
				R.dimen.fab_shadow_offset);
		mTouchDelegateGroup = new TouchDelegateGroup(this);
		setTouchDelegate(mTouchDelegateGroup);
		TypedArray attr = context.obtainStyledAttributes(attributeSet,
				R.styleable.FloatingActionsMenu, 0, 0);
		mAddButtonPlusColor = attr.getColor(
				R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor,
				getColor(android.R.color.white));
		mAddButtonColorNormal = attr.getColor(
				R.styleable.FloatingActionsMenu_fab_addButtonColorNormal,
				getColor(android.R.color.holo_blue_dark));
		mAddButtonColorPressed = attr.getColor(
				R.styleable.FloatingActionsMenu_fab_addButtonColorPressed,
				getColor(android.R.color.holo_blue_light));
		mAddButtonSize = attr.getInt(
				R.styleable.FloatingActionsMenu_fab_addButtonSize,
				FloatingActionButton.SIZE_NORMAL);
		mAddButtonStrokeVisible = attr.getBoolean(
				R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible,
				true);
		mExpandDirection = attr.getInt(
				R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
		mLabelsStyle = attr.getResourceId(
				R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
		mLabelsPosition = attr.getInt(
				R.styleable.FloatingActionsMenu_fab_labelsPosition,
				LABELS_ON_LEFT_SIDE);
		attr.recycle();

		if (mLabelsStyle != 0 && expandsHorizontally()) {
			throw new IllegalStateException(
					"Action labels in horizontal expand orientation is not supported.");
		}

		createAddButton(context);
	}

	public void setOnFloatingActionsMenuUpdateListener(
			OnFloatingActionsMenuUpdateListener listener) {
		mListener = listener;
	}

	private boolean expandsHorizontally() {
		return mExpandDirection == EXPAND_LEFT
				|| mExpandDirection == EXPAND_RIGHT;
	}

	private static class RotatingDrawable extends LayerDrawable {
		public RotatingDrawable(Drawable drawable) {
			super(new Drawable[] { drawable });
		}

		private float mRotation;

		@SuppressWarnings("UnusedDeclaration")
		public float getRotation() {
			return mRotation;
		}

		@SuppressWarnings("UnusedDeclaration")
		public void setRotation(float rotation) {
			mRotation = rotation;
			invalidateSelf();
		}

		@Override
		public void draw(Canvas canvas) {
			canvas.save();
			canvas.rotate(mRotation, getBounds().centerX(), getBounds()
					.centerY());
			super.draw(canvas);
			canvas.restore();
		}
	}

	private void createAddButton(Context context) {
		mAddButton = new AddFloatingActionButton(context) {
			@Override
			void updateBackground() {
				mPlusColor = mAddButtonPlusColor;
				mColorNormal = mAddButtonColorNormal;
				mColorPressed = mAddButtonColorPressed;
				mStrokeVisible = mAddButtonStrokeVisible;
				super.updateBackground();
			}

			@Override
			Drawable getIconDrawable() {
				final RotatingDrawable rotatingDrawable = new RotatingDrawable(
						super.getIconDrawable());
				mRotatingDrawable = rotatingDrawable;

				final OvershootInterpolator interpolator = new OvershootInterpolator();

				final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(
						rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION,
						COLLAPSED_PLUS_ROTATION);
				final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(
						rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION,
						EXPANDED_PLUS_ROTATION);

				collapseAnimator.setInterpolator(interpolator);
				expandAnimator.setInterpolator(interpolator);

				mExpandAnimation.play(expandAnimator);
				mCollapseAnimation.play(collapseAnimator);

				return rotatingDrawable;
			}
		};

		mAddButton.setId(R.id.fab_expand_menu_button);
		mAddButton.setSize(mAddButtonSize);
		mAddButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				toggle();
			}
		});

		addView(mAddButton, super.generateDefaultLayoutParams());
	}

	public void addButton(FloatingActionButton button) {
		addView(button, mButtonsCount - 1);
		mButtonsCount++;
		if (mLabelsStyle != 0) {
			createLabels();
		}
	}

	public void removeButton(FloatingActionButton button) {
		if (button != null) {
			removeView(button.getLabelView());
			removeView(button);
			button.setTag(R.id.fab_label, null);
			mButtonsCount--;
		}
	}
	public void removeButton(int id) {
		FloatingActionButton button=(FloatingActionButton)findViewById(id);
		this.removeButton(button);
	}
	private int getColor(@ColorRes int id) {
		return getResources().getColor(id);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		measureChildren(widthMeasureSpec, heightMeasureSpec);

		int width = 0;
		int height = 0;

		mMaxButtonWidth = 0;
		mMaxButtonHeight = 0;
		int maxLabelWidth = 0;

		for (int i = 0; i < mButtonsCount; i++) {
			View child = getChildAt(i);

			if (child.getVisibility() == GONE) {
				continue;
			}

			switch (mExpandDirection) {
			case EXPAND_UP:
			case EXPAND_DOWN:
				mMaxButtonWidth = Math.max(mMaxButtonWidth,
						child.getMeasuredWidth());
				height += child.getMeasuredHeight();
				break;
			case EXPAND_LEFT:
			case EXPAND_RIGHT:
				width += child.getMeasuredWidth();
				mMaxButtonHeight = Math.max(mMaxButtonHeight,
						child.getMeasuredHeight());
				break;
			}

			if (!expandsHorizontally()) {
				TextView label = (TextView) child.getTag(R.id.fab_label);
				if (label != null) {
					maxLabelWidth = Math.max(maxLabelWidth,
							label.getMeasuredWidth());
				}
			}
		}

		if (!expandsHorizontally()) {
			width = mMaxButtonWidth
					+ (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
		} else {
			height = mMaxButtonHeight;
		}

		switch (mExpandDirection) {
		case EXPAND_UP:
		case EXPAND_DOWN:
			height += mButtonSpacing * (getChildCount() - 1);
			height = adjustForOvershoot(height);
			break;
		case EXPAND_LEFT:
		case EXPAND_RIGHT:
			width += mButtonSpacing * (getChildCount() - 1);
			width = adjustForOvershoot(width);
			break;
		}

		setMeasuredDimension(width, height);
	}

	private int adjustForOvershoot(int dimension) {
		return dimension * 12 / 10;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		switch (mExpandDirection) {
		case EXPAND_UP:
		case EXPAND_DOWN:
			boolean expandUp = mExpandDirection == EXPAND_UP;
			if (changed) {
				mTouchDelegateGroup.clearTouchDelegates();
			}
			int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight()
					: 0;
			// Ensure mAddButton is centered on the line where the buttons
			// should be
			int buttonsHorizontalCenter = mLabelsPosition == LABELS_ON_LEFT_SIDE ? r
					- l - mMaxButtonWidth / 2
					: mMaxButtonWidth / 2;
			int addButtonLeft = buttonsHorizontalCenter
					- mAddButton.getMeasuredWidth() / 2;
			mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft
					+ mAddButton.getMeasuredWidth(),
					addButtonY + mAddButton.getMeasuredHeight());

			int labelsOffset = mMaxButtonWidth / 2 + mLabelsMargin;
			int labelsXNearButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? buttonsHorizontalCenter
					- labelsOffset
					: buttonsHorizontalCenter + labelsOffset;

			int nextY = expandUp ? addButtonY - mButtonSpacing : addButtonY
					+ mAddButton.getMeasuredHeight() + mButtonSpacing;

			for (int i = mButtonsCount - 1; i >= 0; i--) {
				final View child = getChildAt(i);

				if (child == mAddButton || child.getVisibility() == GONE)
					continue;

				int childX = buttonsHorizontalCenter - child.getMeasuredWidth()
						/ 2;
				int childY = expandUp ? nextY - child.getMeasuredHeight()
						: nextY;
				child.layout(childX, childY, childX + child.getMeasuredWidth(),
						childY + child.getMeasuredHeight());

				float collapsedTranslation = addButtonY - childY;
				float expandedTranslation = 0f;

				child.setTranslationY(mExpanded ? expandedTranslation
						: collapsedTranslation);
				child.setAlpha(mExpanded ? 1f : 0f);

				LayoutParams params = (LayoutParams) child.getLayoutParams();
				params.mCollapseDir.setFloatValues(expandedTranslation,
						collapsedTranslation);
				params.mExpandDir.setFloatValues(collapsedTranslation,
						expandedTranslation);
				params.setAnimationsTarget(child);

				View label = (View) child.getTag(R.id.fab_label);
				if (label != null) {
					int labelXAwayFromButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton
							- label.getMeasuredWidth()
							: labelsXNearButton + label.getMeasuredWidth();

					int labelLeft = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelXAwayFromButton
							: labelsXNearButton;

					int labelRight = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton
							: labelXAwayFromButton;

					int labelTop = childY
							- mLabelsVerticalOffset
							+ (child.getMeasuredHeight() - label
									.getMeasuredHeight()) / 2;

					label.layout(labelLeft, labelTop, labelRight, labelTop
							+ label.getMeasuredHeight());

					Rect touchArea = new Rect(Math.min(childX, labelLeft),
							childY - mButtonSpacing / 2, Math.max(childX
									+ child.getMeasuredWidth(), labelRight),
							childY + child.getMeasuredHeight() + mButtonSpacing
									/ 2);
					mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(
							touchArea, child));

					label.setTranslationY(mExpanded ? expandedTranslation
							: collapsedTranslation);
					label.setAlpha(mExpanded ? 1f : 0f);

					LayoutParams labelParams = (LayoutParams) label
							.getLayoutParams();
					labelParams.mCollapseDir.setFloatValues(
							expandedTranslation, collapsedTranslation);
					labelParams.mExpandDir.setFloatValues(collapsedTranslation,
							expandedTranslation);
					labelParams.setAnimationsTarget(label);
				}

				nextY = expandUp ? childY - mButtonSpacing : childY
						+ child.getMeasuredHeight() + mButtonSpacing;
			}
			break;

		case EXPAND_LEFT:
		case EXPAND_RIGHT:
			boolean expandLeft = mExpandDirection == EXPAND_LEFT;

			int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth()
					: 0;
			// Ensure mAddButton is centered on the line where the buttons
			// should be
			int addButtonTop = b - t - mMaxButtonHeight
					+ (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2;
			mAddButton.layout(addButtonX, addButtonTop,
					addButtonX + mAddButton.getMeasuredWidth(), addButtonTop
							+ mAddButton.getMeasuredHeight());

			int nextX = expandLeft ? addButtonX - mButtonSpacing : addButtonX
					+ mAddButton.getMeasuredWidth() + mButtonSpacing;

			for (int i = mButtonsCount - 1; i >= 0; i--) {
				final View child = getChildAt(i);

				if (child == mAddButton || child.getVisibility() == GONE)
					continue;

				int childX = expandLeft ? nextX - child.getMeasuredWidth()
						: nextX;
				int childY = addButtonTop
						+ (mAddButton.getMeasuredHeight() - child
								.getMeasuredHeight()) / 2;
				child.layout(childX, childY, childX + child.getMeasuredWidth(),
						childY + child.getMeasuredHeight());

				float collapsedTranslation = addButtonX - childX;
				float expandedTranslation = 0f;

				child.setTranslationX(mExpanded ? expandedTranslation
						: collapsedTranslation);
				child.setAlpha(mExpanded ? 1f : 0f);

				LayoutParams params = (LayoutParams) child.getLayoutParams();
				params.mCollapseDir.setFloatValues(expandedTranslation,
						collapsedTranslation);
				params.mExpandDir.setFloatValues(collapsedTranslation,
						expandedTranslation);
				params.setAnimationsTarget(child);

				nextX = expandLeft ? childX - mButtonSpacing : childX
						+ child.getMeasuredWidth() + mButtonSpacing;
			}
			break;
		}

	}

	@Override
	protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
		return new LayoutParams(super.generateDefaultLayoutParams());
	}

	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new LayoutParams(super.generateLayoutParams(attrs));
	}

	@Override
	protected ViewGroup.LayoutParams generateLayoutParams(
			ViewGroup.LayoutParams p) {
		return new LayoutParams(super.generateLayoutParams(p));
	}

	@Override
	protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
		return super.checkLayoutParams(p);
	}

	private static Interpolator sExpandInterpolator = new OvershootInterpolator();
	private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(
			3f);
	private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator();

	private class LayoutParams extends ViewGroup.LayoutParams {

		private ObjectAnimator mExpandDir = new ObjectAnimator();
		private ObjectAnimator mExpandAlpha = new ObjectAnimator();
		private ObjectAnimator mCollapseDir = new ObjectAnimator();
		private ObjectAnimator mCollapseAlpha = new ObjectAnimator();
		private boolean animationsSetToPlay;

		public LayoutParams(ViewGroup.LayoutParams source) {
			super(source);

			mExpandDir.setInterpolator(sExpandInterpolator);
			mExpandAlpha.setInterpolator(sAlphaExpandInterpolator);
			mCollapseDir.setInterpolator(sCollapseInterpolator);
			mCollapseAlpha.setInterpolator(sCollapseInterpolator);

			mCollapseAlpha.setProperty(View.ALPHA);
			mCollapseAlpha.setFloatValues(1f, 0f);

			mExpandAlpha.setProperty(View.ALPHA);
			mExpandAlpha.setFloatValues(0f, 1f);

			switch (mExpandDirection) {
			case EXPAND_UP:
			case EXPAND_DOWN:
				mCollapseDir.setProperty(View.TRANSLATION_Y);
				mExpandDir.setProperty(View.TRANSLATION_Y);
				break;
			case EXPAND_LEFT:
			case EXPAND_RIGHT:
				mCollapseDir.setProperty(View.TRANSLATION_X);
				mExpandDir.setProperty(View.TRANSLATION_X);
				break;
			}
		}

		public void setAnimationsTarget(View view) {
			mCollapseAlpha.setTarget(view);
			mCollapseDir.setTarget(view);
			mExpandAlpha.setTarget(view);
			mExpandDir.setTarget(view);

			// Now that the animations have targets, set them to be played
			if (!animationsSetToPlay) {
				mCollapseAnimation.play(mCollapseAlpha);
				mCollapseAnimation.play(mCollapseDir);
				mExpandAnimation.play(mExpandAlpha);
				mExpandAnimation.play(mExpandDir);
				animationsSetToPlay = true;
			}
		}
	}

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();

		bringChildToFront(mAddButton);
		mButtonsCount = getChildCount();

		if (mLabelsStyle != 0) {
			createLabels();
		}
	}

	private void createLabels() {
		Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);

		for (int i = 0; i < mButtonsCount; i++) {
			FloatingActionButton button = (FloatingActionButton) getChildAt(i);
			String title = button.getTitle();

			if (button == mAddButton || title == null
					|| button.getTag(R.id.fab_label) != null)
				continue;

			TextView label = new TextView(context);
			label.setTextAppearance(getContext(), mLabelsStyle);
			label.setText(button.getTitle());
			addView(label);

			button.setTag(R.id.fab_label, label);
		}
	}

	public void collapse() {
		if (mExpanded) {
			mExpanded = false;
			mTouchDelegateGroup.setEnabled(false);
			mCollapseAnimation.start();
			mExpandAnimation.cancel();

			if (mListener != null) {
				mListener.onMenuCollapsed();
			}
		}
	}

	public void toggle() {
		if (mExpanded) {
			collapse();
		} else {
			expand();
		}
	}

	public void expand() {
		if (!mExpanded) {
			mExpanded = true;
			mTouchDelegateGroup.setEnabled(true);
			mCollapseAnimation.cancel();
			mExpandAnimation.start();

			if (mListener != null) {
				mListener.onMenuExpanded();
			}
		}
	}

	public boolean isExpanded() {
		return mExpanded;
	}

	@Override
	public Parcelable onSaveInstanceState() {
		Parcelable superState = super.onSaveInstanceState();
		SavedState savedState = new SavedState(superState);
		savedState.mExpanded = mExpanded;

		return savedState;
	}

	@Override
	public void onRestoreInstanceState(Parcelable state) {
		if (state instanceof SavedState) {
			SavedState savedState = (SavedState) state;
			mExpanded = savedState.mExpanded;
			mTouchDelegateGroup.setEnabled(mExpanded);

			if (mRotatingDrawable != null) {
				mRotatingDrawable
						.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION
								: COLLAPSED_PLUS_ROTATION);
			}

			super.onRestoreInstanceState(savedState.getSuperState());
		} else {
			super.onRestoreInstanceState(state);
		}
	}

	public static class SavedState extends BaseSavedState {
		public boolean mExpanded;

		public SavedState(Parcelable parcel) {
			super(parcel);
		}

		private SavedState(Parcel in) {
			super(in);
			mExpanded = in.readInt() == 1;
		}

		@Override
		public void writeToParcel(@NonNull Parcel out, int flags) {
			super.writeToParcel(out, flags);
			out.writeInt(mExpanded ? 1 : 0);
		}

		public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {

			@Override
			public SavedState createFromParcel(Parcel in) {
				return new SavedState(in);
			}

			@Override
			public SavedState[] newArray(int size) {
				return new SavedState[size];
			}
		};
	}
	/*
	 * @Override protected void onLayout(boolean arg0, int arg1, int arg2, int
	 * arg3, int arg4) { // TODO Auto-generated method stub for (int i =
	 * mButtonsCount - 1; i >= 0; i--) { View view=getChildAt(i);
	 * 
	 * } }
	 */
}
